﻿' ----------------------------------------------------------------------
'
' FileStream.vb
'
'   Dbfs.Domain
'
'   This class contains the implementation of a single folder.
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    05/03/11  09:35:00
' ident:    "@(#)/Dbfs/Domain/FileStream.vb 1.0 0.1 05/03/11 0 rjs"

Imports System
Imports System.IO
Imports System.Net
Imports System.Security.AccessControl
Imports Dbfs.Data
Imports Dbfs.Security


Namespace Dbfs

    ''' <summary>
    ''' (DBFS) Exposes instance methods for creating, moving, and enumerating through 
    ''' directories and subdirectories. This class cannot be inherited.
    ''' </summary>
    ''' <remarks>Use the DirectoryInfo class for typical operations such as 
    ''' copying, moving, renaming, creating, and deleting directories.</remarks>
    <SerializableAttribute()> _
    Public Class FileStream
        Inherits Stream

        Private _myBuffer() As Byte
        Private _myLength As Integer
        Private _myOffset As Integer
        Private _bufsize As Integer

        Private _connection As DbfsConnection

        ''' <summary>
        ''' The <see cref="FileSystemEntity"/> as cast to a <see cref="FileEntity"/>.
        ''' </summary>
        ''' <remarks></remarks>
        Private _entity As FileEntity

        Private _path As String


#Region " Constructors "
        ''' <summary>
        ''' Initializes a new instance of the <see cref="FileInfo"/> class.
        ''' </summary>
        ''' <param name="path">A string specifying the path on which to create the <b>DirectoryInfo</b>.</param>
        ''' <remarks>If the path is fully qualified with a UNC name, a folder is created that
        ''' can be persisted in that filesystem.  If the path does not contain a UNC name,
        ''' a folder is created within the virtual space for the current user.</remarks>
        Public Sub New(ByVal path As String)
            MyBase.New()

            ' check for null path
            If path Is Nothing Then
                Throw New ArgumentNullException("path")
            End If

            ' check for valid path characters
            path = path.Trim()
            If path.Length = 0 OrElse _
               path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) > 0 Then
                Throw New ArgumentException("Invalid path.")
            End If

            ' complete the initialization of the object
            Initialize(path)

            _entity = _connection.Context.Read(Of FileEntity)(path)
            Dim stream As Stream = _entity.OpenRead()
            _myBuffer = New Byte((stream.Length) - 1) {}
            _myOffset = 0
            _myLength = stream.Length
            _bufsize = _myLength

            Using br As New BinaryReader(stream)
                br.Read(_myBuffer, 0, _myLength)
            End Using

        End Sub
#End Region ' Constructors

#Region " Properties "
        Public Overrides ReadOnly Property CanRead() As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides ReadOnly Property CanSeek() As Boolean
            Get
                Return True
            End Get
        End Property

        ''' <summary>
        ''' Gets a value that determines whether the current stream can time out.
        ''' </summary>
        ''' <value>A value that determines whether the current stream can time out.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property CanTimeout() As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides ReadOnly Property CanWrite() As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides ReadOnly Property Length() As Long
            Get
                Return _myLength
            End Get
        End Property

        Public Overrides Property Position() As Long
            Get
                Return _myOffset
            End Get
            Set(ByVal value As Long)
                _myOffset = value
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the name of this folder.
        ''' </summary>
        ''' <value>A String containing the name for this folder.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Name() As String
            Get
                Return _path
            End Get
        End Property
#End Region ' Properties

#Region " Public Methods "
        Public Overrides Sub Close()
            MyBase.Close()
            Flush()
            _connection.Context.Create(_entity)
        End Sub

        Public Overrides Sub Flush()
            Dim _stream As Stream = _entity.OpenWrite()
            Using bw As New BinaryWriter(_stream)
                bw.Write(_myBuffer, 0, _myLength)
            End Using
            _stream.Flush()
        End Sub

        ''' <summary>
        ''' Gets a <see cref="FileSecurity"/> object that encapsulates the 
        ''' access control list (ACL) entries for the directory described by the 
        ''' current <see cref="FileInfo"/> object.
        ''' </summary>
        ''' <returns>A <see cref="FileSecurity"/> object that encapsulates 
        ''' the access control rules for the directory.</returns>
        ''' <remarks></remarks>
        Public Function GetAccessControl() As Security.FileSystemSecurity
            ' apply security, check to see if we can read the access control list
            'If (AccessRights And FileSystemRights.ReadPermissions) = 0 Then
            '    Throw New DbfsException("Access Denied")
            'End If
            If _entity.FileSystemSecurity Is Nothing Then
                Throw New SystemException("Invalid security")
            End If
            Return _entity.FileSystemSecurity
        End Function

        Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
            Dim nBytesLeftInBuffer = _myLength - _myOffset
            Dim nBytesToRead = count
            If count > nBytesLeftInBuffer Then
                nBytesToRead = nBytesLeftInBuffer
            End If
            System.Buffer.BlockCopy(_myBuffer, _myOffset, buffer, offset, nBytesToRead)
            _myOffset += nBytesToRead
            If _myOffset > _myLength Then
                _myOffset = _myLength
            End If
            Return nBytesToRead
        End Function

        Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
            Throw New NotImplementedException()
        End Function

        ''' <summary>
        ''' Applies access control list (ACL) entries described by a <see cref="FileSecurity"/>
        ''' object to the file described by the current <see cref="FileInfo"/> object.
        ''' </summary>
        ''' <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes 
        ''' an access control list (ACL) entry to apply to the current file.</param>
        ''' <remarks></remarks>
        Public Sub SetAccessControl(ByVal fileSecurity As System.Security.AccessControl.FileSystemSecurity)
            Dim mySecurity As DbfsFileSecurity = New DbfsFileSecurity(fileSecurity)
            Throw New NotImplementedException()
        End Sub

        Public Overrides Sub SetLength(ByVal value As Long)
            Throw New NotImplementedException()
        End Sub

        Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
            Dim size As Integer = _myOffset + count
            If size > _bufsize Then
                _bufsize = (_bufsize \ 1024) * 1024
                _bufsize += 1024
                Array.Resize(_myBuffer, _bufsize)
            End If
            System.Buffer.BlockCopy(buffer, offset, _myBuffer, _myOffset, count)
            _myOffset += count
            If _myOffset > _myLength Then
                _myLength = _myOffset
            End If
        End Sub
#End Region ' Public Methods

#Region " Private Methods "
        Private Sub Initialize(ByVal path As String)

            ' local variables
            Dim fileSystemName As String

            ' initialize variables
            fileSystemName = String.Empty
            _path = path

            ' the dbfs instance to connect to is given by the high level
            ' qualifier in the path using the UNC convention
            If path.StartsWith(UNC_CHARS) Then
                path = path.Substring(2)
                Dim i = path.IndexOfAny(DIRECTORY_SEP_CHARS)
                If i > 0 Then
                    fileSystemName = path.Substring(0, i)
                Else
                    fileSystemName = path
                End If
            End If

            ' establish the database connection
            _connection = New DbfsConnection(fileSystemName)

        End Sub
#End Region ' Private Methods

    End Class

End Namespace
